{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "08daf969-81ad-4f56-bceb-21e723a5f681",
   "metadata": {},
   "source": [
    "### 代码练习: 使用OpenCV实现SVM "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "ea44e8cf-2b88-42c8-9127-7dd399f98e03",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sklearn SVM 识别手写字母集的准确率是 95.0 %, 测试用时: 26.03 秒\n",
      "OpenCV SVM 识别手写字母集的准确率是 94.15 %, 测试用时: 10.53 秒\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "#import pandas as pd\n",
    "from sklearn.datasets import load_digits\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.neighbors import KNeighborsClassifier \n",
    "import time\n",
    "import math\n",
    "\n",
    "##sklearn SVM识别手写字母集\n",
    "#读取数据集\n",
    "#df= pd.read_csv('letter-recognition.data', sep=',',header=None)\n",
    "df = np.loadtxt('letter-recognition.data', dtype='float32', delimiter=',',converters={0: lambda ch: ord(ch) - ord('A')})\n",
    "\n",
    "# 1.获取X和y\n",
    "X = df[:,1:]\n",
    "y = df[:,0]\n",
    "\n",
    "# 2. train_test_split\n",
    "X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.3)\n",
    "\n",
    "#开始计时\n",
    "start = time.time()\n",
    "\n",
    "# 3. 创建KNN模型\n",
    "PredictList = [] #预测率\n",
    "\n",
    "K = np.arange(1,int(math.sqrt(400)))\n",
    "for i in K:\n",
    "    knn = KNeighborsClassifier(n_neighbors=i)\n",
    "    knn.fit(X_train,y_train)   \n",
    "    #准确率\n",
    "    acc=knn.score(X_test,y_test).round(2)\n",
    "    #print(\"当K=\",K,\"的时候此knn模型的准确率为\",acc)\n",
    "    PredictList.append(acc)   \n",
    "PredictList = np.array(PredictList)\n",
    "\n",
    "#计时结束\n",
    "end = time.time()\n",
    "#所用时间\n",
    "times = end - start\n",
    "\n",
    "print(\"sklearn SVM 识别手写字母集的准确率是\",PredictList.max()*100,'%,',\"测试用时:\",round(times,2),\"秒\")\n",
    "\n",
    "\n",
    "##OpenCV SVM识别手写字母集\n",
    "# 加载数据并将字母转换为数字\n",
    "data= np.loadtxt('letter-recognition.data', dtype= 'float32', delimiter = ',', converters= {0: lambda ch: ord(ch)-ord('A')})\n",
    "\n",
    "# 将数据集一分为二，训练集和测试集各有 10000 个样本\n",
    "train, test = np.vsplit(data,2)\n",
    "\n",
    "# 将训练数据和测试数据拆分为特征和响应\n",
    "responses, trainData = np.hsplit(train,[1])\n",
    "labels, testData = np.hsplit(test,[1])\n",
    "\n",
    "#开始计时\n",
    "start = time.time()\n",
    "\n",
    "PredictList = []\n",
    "\n",
    "K = np.arange(1,int(np.sqrt(train.shape[0])))\n",
    "\n",
    "for i in K:\n",
    "    knn = cv.ml.KNearest_create()\n",
    "    knn.train(trainData, cv.ml.ROW_SAMPLE, responses)\n",
    "    \n",
    "    ret, result, neighbours, dist = knn.findNearest(testData, k=i)\n",
    "    correct = np.count_nonzero(result == labels)\n",
    "    accuracy = correct/10000\n",
    "    PredictList.append(accuracy)\n",
    "PredictList = np.array(PredictList)\n",
    "\n",
    "#计时结束\n",
    "end = time.time()\n",
    "#所用时间\n",
    "times = end - start\n",
    "\n",
    "print(\"OpenCV SVM 识别手写字母集的准确率是\",PredictList.max()*100,'%,',\"测试用时:\",round(times,2),\"秒\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ab1a9069-a90c-459d-afe1-60ffda249c0d",
   "metadata": {},
   "source": [
    "#### 参考答案"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ec280b88-1fc1-466a-9ffc-0f0cf452577f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sklearn KNN 识别手写字母集的准确率是93.42%, 测试用时: 93.42秒\n",
      "OpenCV KNN 识别手写字母集的准确率是93.59%, 测试用时: 93.59秒\n",
      "sklearn SVC 识别手写字母集的准确率是93.77%, 测试用时: 93.77秒\n",
      "OpenCV SVC 识别手写字母集的准确率是96.63%, 测试用时: 96.63秒\n"
     ]
    }
   ],
   "source": [
    "import time\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.model_selection import train_test_split\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import cv2 as cv\n",
    "from sklearn.svm import SVC\n",
    "\n",
    "# 读取数据\n",
    "# # 参考按照 OpenCV 官网方法如下\n",
    "# data= np.loadtxt('letter-recognition.data', dtype= 'float32', delimiter = ',',\n",
    "#                     converters= {0: lambda ch: ord(ch)-ord('A')})\n",
    "# X = data[:,1:]\n",
    "# y = data[:,0]\n",
    "data = pd.read_csv('letter-recognition.data',header=None)\n",
    "X = data.drop(0,axis=1).values.astype(np.float32)\n",
    "y = np.array([ord(x)-ord('A') for x in data[0]]).astype(np.int)\n",
    "X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.50)\n",
    "\n",
    "#########################################\n",
    "start1 = time.time()\n",
    "knn = KNeighborsClassifier(n_neighbors=5)\n",
    "knn.fit(X_train,y_train)\n",
    "knn_acc=knn.score(X_test,y_test.ravel())*100\n",
    "end1 = time.time()\n",
    "print(\"sklearn KNN 识别手写字母集的准确率是{0:.2f}%, 测试用时: {0:.2f}秒\".format(knn_acc,end1 - start1))\n",
    "\n",
    "#########################################\n",
    "start2 = time.time()\n",
    "knn = cv.ml.KNearest_create()\n",
    "knn.train(X_train, cv.ml.ROW_SAMPLE, y_train)\n",
    "ret, result, neighbours, dist = knn.findNearest(X_test, k=5)\n",
    "correct = np.count_nonzero(result.ravel() == y_test)\n",
    "opencv_knn_acc = correct*100/10000\n",
    "end2 = time.time()\n",
    "print(\"OpenCV KNN 识别手写字母集的准确率是{0:.2f}%, 测试用时: {0:.2f}秒\".format(opencv_knn_acc,end2 - start2))\n",
    "\n",
    "#########################################\n",
    "start3 = time.time()\n",
    "svc = SVC(C=2.67)\n",
    "svc.fit(X_train,y_train)\n",
    "svc_acc=svc.score(X_test,y_test.ravel())*100\n",
    "end3 = time.time()\n",
    "print(\"sklearn SVC 识别手写字母集的准确率是{0:.2f}%, 测试用时: {0:.2f}秒\".format(svc_acc,end3 - start3))\n",
    "\n",
    "#########################################\n",
    "start4 = time.time()\n",
    "svm = cv.ml.SVM_create()\n",
    "# svm.setKernel(cv.ml.SVM_LINEAR) # 如果使用 linear 我们发现准确率是60%左右\n",
    "# 经过调研 https://docs.opencv.org/3.4/d1/d2d/classcv_1_1ml_1_1SVM.html\n",
    "# 我们发现，如果想要使用 rbf 核，应该提供的参数是 CHI2\n",
    "svm.setKernel(cv.ml.SVM_CHI2)\n",
    "svm.setType(cv.ml.SVM_C_SVC)\n",
    "svm.setC(2.67) \n",
    "svm.train(X_train, cv.ml.ROW_SAMPLE, y_train)\n",
    "y_predict = svm.predict(X_test)\n",
    "# print(y_predict)\n",
    "correct = np.count_nonzero(y_predict[1].ravel() == y_test)\n",
    "opencv_svm_acc = correct*100/10000\n",
    "end4 = time.time()\n",
    "print(\"OpenCV SVC 识别手写字母集的准确率是{0:.2f}%, 测试用时: {0:.2f}秒\".format(opencv_svm_acc,end4 - start4))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
